﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using BusinessLogic;
using Otgruzka.ServicesClients;
using BusinessLogic.Security;
using Otgruzka.Controls.jQuery;
using Common.Export;
using System.Windows;
using System.Windows.Media;
using Otgruzka.Export;
using System.Windows.Controls;
using Otgruzka.Helpers;
using Common;
using System.Configuration;
using DataTier.Entities;
using System.ServiceModel;
using DataTier;


namespace Otgruzka.Controls.Order
{
    public partial class OrderDetails : System.Web.UI.UserControl
    {
        public int countid = 2;
        protected OrderEntity SavedOrder = null;

        /// <summary>
        /// Возвращает ID открытого документа. Если документ Новый, то возвращается 0
        /// </summary>
        public int Id
        {
            get 
            {
                return (!string.IsNullOrEmpty(hId.Value) ? Convert.ToInt32(hId.Value) : 0);
            }
        }

        public string StatusName
        {
            get
            {
                var client = new OrdersClient();
                try
                {
                    // Получение ID заказа из адресной строки
                    int orderId = Convert.ToInt32(Request.QueryString["oid"]);

                    // Получение Заказа из БД
                    var order = client.GetOrder(UserPrincipal.Current.UserId, orderId);
                    if (order != null)
                    {
                        return order.State.Name;
                    }
                    return "Error";
                }
                catch (System.Exception exp)
                {
                    // Неправильно. Тут надо заполнять контрол вывода ошибки, а return просто Error
                    this.ErrorMessage.Text = exp.Message;
                    return "Error";
                }
            }
        }

        /// <summary>
        /// Возвращает Статус документа. Если статус не установлен вообще, то возвращается 0
        /// </summary>
        public int StatusId
        {
            get
            {
                return (!string.IsNullOrEmpty(hStateId.Value) ? Convert.ToInt32(hStateId.Value) : 0);
            }
        }

        public string ContactNameClientID { get { return this.ContactName.ClientID; } }
        public string ContactNameString { get { return this.ContactName.Text; } }


        /// <summary>
        /// Возвращает Признак того, есть ли в Заказе товары не из списка (отсутствующие в каталоге товары)
        /// </summary>
        public bool ExistsIsUnavailable
        {
            get
            {
                return (!string.IsNullOrEmpty(hExistsIsUnavailable.Value) ? Convert.ToBoolean(hExistsIsUnavailable.Value) : false);
            }
        }


        
        /// <summary>
        /// Свойство-заголовок формы Заказа
        /// </summary>
        protected string HeaderText
        {
            get 
            {
                if (Request.QueryString["oid"] != null)
                    return "Редактирование заказа";
                else
                    return "Создание нового заказа";
            }
        }


        /// <summary>
        /// Возвращает Признак того, что Заказ редактируется администратором
        /// </summary>
        public bool ModifiedByAdmin
        {
            get
            {
                return 
                    (
                        ((!string.IsNullOrEmpty(this.hIsModifiedByAdmin.Value) && bool.Parse(this.hIsModifiedByAdmin.Value)) || 
                        (Request.QueryString["modif"] != null && Request.QueryString["modif"].ToLower() == "true")) 
                        ? true : false
                    );
            }
        }
        

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
                BindUserInfo();

            if (!IsPostBack)
                BindCostsCenterList(); // Список авторов заполняется

            if (UserPrincipal.Current.CreateFromOther && Request.QueryString["oid"] == null) // Новый Заказ. Предоставляется возможность выбора автора. Т.е. делается видимым выпадающий список
            {
                ddlCostsCenters.Visible = true;
                CostsCenterNameWithDepartmentName.Visible = false;
                labelAuthor.Visible = false;
            }
            else // Существующий заказ. Просто отображается автор Заказа
            {
                ddlCostsCenters.Visible = false;
                CostsCenterNameWithDepartmentName.Visible = true;
                labelAuthor.Visible = true;
            }

            if (Request.QueryString["oid"] != null && !IsPostBack)
            {
                BindOrder();
            }

            if (IsPostBack)
            {
                this.SavedOrder = GetSavedOrder();
            }
            //ProductsList = GetData();
            //Bind();

            if (Session["OkMessage"] != null) this.OkMessage.Text = Session["OkMessage"].ToString();
            Session["OkMessage"] = null;
        }


        protected override void OnPreRender(EventArgs e)
        {
        }


        private CostsCenterFullInfoEntity GetCostsCenterByUserId(int userId)
        {
            var client = new DictionariesClient();
            return client.GetCostsCenterFullInfoByUserId(UserPrincipal.Current.UserId, userId);
        }


        private void BindCostsCenterList()
        {
            var client = new DictionariesClient();
            var costsCenters = client.GetCostsCenterFullInfoList(UserPrincipal.Current.UserId);
            if (costsCenters != null && costsCenters.Count > 0)
            {
                // Заполнение выпадающего списка
                ddlCostsCenters.DataSource = costsCenters;
                ddlCostsCenters.DataBind();

                // Выставляется ткущее состояние в списке авторов Заказа
                if (ddlCostsCenters.DataSource != null)
                {
                    ddlCostsCenters.SelectedValue = UserPrincipal.Current.UserId.ToString();
                    CostsCenterNameWithDepartmentName.Text = ddlCostsCenters.SelectedItem.Text;
                }
            }
        }


        /// <summary>
        /// Заполнение полей о авторизованном пользователе: Юр.лицо, Департамент, Центр Затрат
        /// </summary>
        private void BindUserInfo()
        {
            //UserPrincipal.Current.CreateFromOther
            lJuridicalPersonName.Text += string.Format("<b>{0}</b>", UserPrincipal.Current.JuridicalPersonName);
            lDepartmentName.Text += string.Format("<b>{0}</b>", UserPrincipal.Current.DepartmentName);
            lCostCenterName.Text += string.Format("<b>{0}</b>", UserPrincipal.Current.CostCenterName);

            //this.JuridicalPersonName.Text = UserPrincipal.Current.JuridicalPersonName;
            //this.DepartmentName.Text = UserPrincipal.Current.DepartmentName;
            //this.CostCenterName.Text = UserPrincipal.Current.CostCenterName;
            this.DepartmentOfficeNumber.Text = UserPrincipal.Current.DepartmentOfficeNumber;
        }


        /// <summary>
        /// Заполнение полей формы данными из Заказа
        /// </summary>
        public void BindOrder()
        {
            // Получение ID заказа из адресной строки
            int orderId = Convert.ToInt32(Request.QueryString["oid"]);
            

            var client = new OrdersClient();
            try
            {
                // Получение Заказа из БД
                var order = client.GetOrder(UserPrincipal.Current.UserId, orderId);
                if (order != null)
                {
                    Session["CurrentOrderEntity"] = order;

                    this.hIsModifiedByAdmin.Value = order.Header.IsModifiedByAdmin.ToString();
                    if (Request.QueryString["modif"] != null && Request.QueryString["modif"].ToLower() == "true")
                        this.hIsModifiedByAdmin.Value = true.ToString();

                    List<ExpendBillEntity> ExpendBills = null;
                    if (this.ModifiedByAdmin)
                    {
                        // Выборка исполненных накладных, выписанных из данного Заказа.
                        ExpendBills = GetExpendBillEntityList(orderId);
                        if (ExpendBills != null)
                            ExpendBills = ExpendBills.Where(b => b.State.Id == (int)StateEnum.ExpendBillExecuted).ToList();
                    }


                    // Заполнение формы общими данными о Заказе (Header)
                    this.OrderNumber.Text = order.Header.Number;
                    this.OrderDate.Text = string.Format("{0:dd.MM.yyyy}", order.Header.CreatedDate);
                    this.ContactName.Text = order.Header.ContactName;
                    this.ContactPhone.Text = order.Header.ContactPhone;
                    this.ContactEmail.Text = order.Header.ContactEmail;
                    
                    this.AccountNumber.Text = order.Header.AccNumber;
                    this.AccountDate.Text = string.Format("{0:dd.MM.yyyy}", order.Header.AccCreatedDate);

                    if (!string.IsNullOrEmpty(order.Header.Description))
                    {
                        this.DescriptionLabel.Text = order.Header.Description;

                        string str = "Номер офиса, этаж: ";
                        if (order.Header.Description.Contains(str))
                            this.DepartmentOfficeNumber.Text = order.Header.Description.Substring(str.Length);
                        else
                            this.DepartmentOfficeNumber.Text = order.Header.Description;
                    }

                    // В скрытые поля запоминаются системные данные о Заказе, чтобы при сохранении вернуть их в БД
                    this.hCreatedDate.Value = order.Header.CreatedDate.ToString();
                    this.hCreatedUserId.Value = order.Header.CreatedUserId.ToString();
                    //this.hDescription.Value = order.Header.Description;
                    this.hId.Value = order.Header.Id.ToString();
                    this.hNumber.Value = order.Header.Number;
                    this.hStateId.Value = order.Header.StateId.ToString();
                    this.hExistsIsUnavailable.Value = (order.Specification.Where(s => s.Specification.IsUnavailable == true).Count() > 0).ToString();

                    int count = 0;
                    string retstr = "";
                    // Заполнение формы данными из БД
                    foreach (var s in order.Specification)
                    {
                        if (retstr != "") retstr += "^";
                        //s.Specification.Count
                        //s.Specification.RowId
                        //s.Specification.Summa

                        retstr += s.Specification.ProductId + "^";
                        retstr += s.Specification.Artikul + "^";
                        retstr += s.Specification.ProductName + "^";
                        retstr += s.Specification.Price + "^";
                        retstr += s.Specification.Count + "^";

                        // Если происходит редактирование админом
                        if (this.ModifiedByAdmin)
                        {
                            var countInBills = ExpendBills.Select(b => b.Specification.Where(bs => bs.ProductId == s.Specification.ProductId).Sum(bp => bp.Count)).Sum();
                            
                            if (countInBills >= s.Specification.Count)
                                retstr += "0";
                            else
                                retstr += "1";
                        }
                        else
                            retstr += "1";

                        count++;
                    }
                    this.arraycount.Value = count.ToString();
                    this.arraydata.Value = retstr;

                    // Отображается автор Заказа
                    var center = GetCostsCenterByUserId(order.Header.CreatedUserId);
                    if (center != null)
                        CostsCenterNameWithDepartmentName.Text = center.CostsCenterNameWithDepartmentName;
                }
            }
            catch (System.ServiceModel.FaultException<ExceptionDetail> ex)
            {
                string message = client.GetExceptionDetail(ex);
                this.ErrorMessage.Text = message;
            }
            catch (System.Exception exp)
            {
                this.ErrorMessage.Text = exp.Message;
            }
        }


        private List<ExpendBillEntity> GetExpendBillEntityList(int orderId)
        {

            var client = new ExpendBillsClient();
            try
            {
                var expendBillList = client.GetExpendBillList(UserPrincipal.Current.UserId, false, orderId);
                return expendBillList;
            }
            catch (System.ServiceModel.FaultException<ExceptionDetail> ex)
            {
                string message = client.GetExceptionDetail(ex);
                this.ErrorMessage.Text = message;
                return null;
            }
            catch (System.Exception exp)
            {
                this.ErrorMessage.Text = exp.Message;
                return null;
            }

        }


        /// <summary>
        /// Возвращает список объектов OrderSpec
        /// </summary>
        /// <returns></returns>
        private List<OrderSpec> GetSpecification()
        {
            string[] stringSeparators = new string[] { "##" };
            //rowID:1||ProductId:1||Count:1||Summa:100##rowID:2||ProductId:2||Count:2||Summa:400##rowID:3||ProductId:3||Count:3||Summa:900##

            string spec = this.rettoserv.Value;
            if (spec.Length >= 2)
            {
                while ((spec.Substring(spec.Length - 2, 2) == "##") || (spec.Substring(spec.Length - 2, 2) == "||"))
                {
                    spec = spec.Substring(0, spec.Length - 2);
                }
            }

            string[] specArrayString = spec.Split(stringSeparators, StringSplitOptions.RemoveEmptyEntries);

            if (specArrayString == null || specArrayString.Length <= 0)
                return null;

            var listOrderSpec = new List<OrderSpec>();

            stringSeparators = new string[] { "||" };
            foreach (string specString in specArrayString)
            {
                OrderSpec orderSpec = new OrderSpec();

                string[] specProperties = specString.Split(stringSeparators, StringSplitOptions.RemoveEmptyEntries);
                foreach(string proderty in specProperties)
                {
                    string name = proderty.Split(':')[0];
                    string value = proderty.Split(':')[1];

                    switch (name.ToLower())
                    {
                        case "rowid":
                            orderSpec.RowId = Convert.ToInt32(value); //Номер строки с товаром
                            break;
                        case "productid":
                            orderSpec.ProductId = Convert.ToInt32(value); //Id товара
                            break;
                        case "count":
                            orderSpec.Count = Convert.ToInt32(value); //заказываемое количество товара
                            break;
                        case "summa":
                            //orderSpec.Summa = Convert.ToDouble(value.Replace(',', '.')); //сумма товара (цена * количество)
                            orderSpec.Summa = value.GetDoubleFromString(); //сумма товара (цена * количество)
                            orderSpec.IsUnavailable = (orderSpec.Summa == 0.0);
                            
                            break;
                        default:
                            break;
                    }
                }
                listOrderSpec.Add(orderSpec);
            }

            return listOrderSpec;
        }


        /// <summary>
        /// Возвращает общую сумму по заказываемым товарам
        /// </summary>
        /// <param name="products"></param>
        /// <returns></returns>
        private double GetItogoSumm(List<OrderSpec> products)
        {
            if (products == null || products.Count <= 0)
                return 0.0;

            double intogo = 0.0;
            foreach (var product in products)
            {
                //intogo += (product.Summa.HasValue) ? product.Summa.Value : 0.0;
                intogo += product.Summa;
            }
            return intogo;
        }


        /// <summary>
        /// Возвращает объект OrderEntity для сохранения в базу
        /// </summary>
        /// <returns></returns>
        private OrderEntity GetSavedOrder()
        {
            var listSpec = GetSpecification();
            var itogoSumm = GetItogoSumm(listSpec);

            //OrdersServRef.OrderEntity order = (this.SavedOrder !=null) ? this.SavedOrder : new OrdersServRef.OrderEntity();
            //order.Header = (this.SavedOrder != null && this.SavedOrder.Header != null) ? this.SavedOrder.Header : new OrdersServRef.Order();
            OrderEntity order = new OrderEntity();
            order.Header = new DataTier.Order();

            if (!string.IsNullOrEmpty(this.ContactName.Text))
                order.Header.ContactName = this.ContactName.Text;
            if (!string.IsNullOrEmpty(this.ContactPhone.Text))
                order.Header.ContactPhone = this.ContactPhone.Text;
            if (!string.IsNullOrEmpty(this.ContactEmail.Text))
                order.Header.ContactEmail = this.ContactEmail.Text;

            if (!string.IsNullOrEmpty(this.DepartmentOfficeNumber.Text))
                order.Header.Description = string.Format("Номер офиса, этаж: {0}", this.DepartmentOfficeNumber.Text);

            if (!string.IsNullOrEmpty(this.hId.Value))
            {
                order.Header.CreatedDate = Convert.ToDateTime(this.hCreatedDate.Value);
                order.Header.CreatedUserId = int.Parse(this.hCreatedUserId.Value);
                //order.Header.Description = this.hDescription.Value;
                order.Header.Id = int.Parse(this.hId.Value);
                order.Header.Number = this.hNumber.Value;
                order.Header.StateId = int.Parse(this.hStateId.Value);

                order.Header.IsModifiedByAdmin = bool.Parse(this.hIsModifiedByAdmin.Value);

                order.Header.ModifiedUserId = UserPrincipal.Current.UserId;//Id пользователя, зарегистрированного в системе
                order.Header.ModifiedDate = DateTime.Now;
            }
            else
            {
                if (UserPrincipal.Current.CreateFromOther)
                    order.Header.CreatedUserId = Convert.ToInt32(ddlCostsCenters.SelectedValue);
                else
                    order.Header.CreatedUserId = UserPrincipal.Current.UserId;//Id пользователя, зарегистрированного в системе

                order.Header.CreatedDate = DateTime.Now;
                order.Header.StateId = (int)StateEnum.Project;
            }

            order.Header.ItogoSumm = itogoSumm;// Итоговая сумма по Заказу

            if (listSpec != null && listSpec.Count > 0)
            {
                List<OrderSpecEntity> listSpecEntity = new List<OrderSpecEntity>();
                foreach (var spec in listSpec)
                {
                    listSpecEntity.Add(new OrderSpecEntity() { Specification = spec });
                }
                order.Specification = listSpecEntity;
            }

            return order;
        }


        /// <summary>
        /// Событие нажатия кнопки Сохранить заказ
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void btnSaveOrder_Click(object sender, EventArgs e)
        {
            string validateEMail = ValidationHelper.ValidateEMail(this.ContactEmail.Text);


            if (!string.IsNullOrEmpty(validateEMail)) // Если некорректный формат e-mail
            {
                ErrorMessage.Text = "Некорректный адрес электронной почты получателя заказа";
            }
            else
            {
                var savedOrder = (this.SavedOrder != null) ? this.SavedOrder : GetSavedOrder();

                if (this.ModifiedByAdmin) // Заказ модифицируется администратором
                {
                    // проверка на корректность ввода в ячейке количества товара. 
                    // Если количество введено меньше, чем есть в сумме в исполненных накладных, то выводить соответствующее ругательство
                    string validCounts = ValidateProductsCount(savedOrder);
                    if (!string.IsNullOrEmpty(validCounts))
                    {
                        ErrorMessage.Text = string.Format("Некорректное заполнение количества товара: <br />", validCounts);
                    }
                    else
                    {
                        SaveOrder(savedOrder);
                    }
                }
                else
                {
                    SaveOrder(savedOrder);
                }
            }
        }


        /// <summary>
        /// Проверка на корректность ввода в ячейке количества товара. 
        /// </summary>
        /// <param name="savedOrder"></param>
        /// <returns></returns>
        private string ValidateProductsCount(OrderEntity savedOrder)
        {
            string message = "";
            List<ExpendBillEntity> ExpendBills = null;

            // Выборка исполненных накладных, выписанных из данного Заказа.
            ExpendBills = GetExpendBillEntityList(savedOrder.Header.Id);
            if (ExpendBills != null)
                ExpendBills = ExpendBills.Where(b => b.State.Id == (int)StateEnum.ExpendBillExecuted).ToList();

            foreach (var orderSpec in savedOrder.Specification)
            {
                var countInBills = ExpendBills.Select(b => b.Specification.Where(bs => bs.ProductId == orderSpec.Specification.ProductId).Sum(bp => bp.Count)).Sum();

                if (orderSpec.Specification.Count < countInBills) // Введенное количество товара меньше, чем было отгружено по исполненным накладным
                {
                    message += string.Format("{0}У товара '{1}' количество введено меньше, чем уже было отгружено! Было отгружено {2}!", 
                                                (!string.IsNullOrEmpty(message) ? "<br />" : "" ),                    
                                                orderSpec.Specification.ProductName, 
                                                countInBills);
                }
            }

            foreach (var bill in ExpendBills)
            {
                foreach (var spec in bill.Specification)
                {
                    if (!savedOrder.Specification.Where(s => s.Specification.ProductId == spec.ProductId).Any())
                    {
                        message += string.Format("{0}Товар '{1}' присутствует в исполненных накладных. Удалять его невозможно!",
                                                (!string.IsNullOrEmpty(message) ? "<br />" : ""),
                                                spec.ProductName);
                    }
                }
            }

            return message;
        }



        /// <summary>
        /// Проверка полностью ли отгружен Заказ
        /// </summary>
        /// <param name="order"></param>
        /// <param name="ExpendBills"></param>
        /// <returns></returns>
        private bool IsFullDispatchOrder(OrderEntity order, List<ExpendBillEntity> ExpendBills)
        {
            if (ExpendBills == null)
                return false;

            ExpendBills = ExpendBills.Where(b => b.State.Id == (int)StateEnum.ExpendBillExecuted).ToList();

            foreach (var orderSpec in order.Specification)
            {
                var countInBills = ExpendBills.Select(b => b.Specification.Where(bs => bs.ProductId == orderSpec.Specification.ProductId).Sum(bp => bp.Count)).Sum();

                if (orderSpec.Specification.Count > countInBills) // Введенное количество товара больше, чем было отгружено по исполненным накладным. Можно заканчивать проверку и возвращать FALSE
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Метод сохранения Заказа путем вызова сервиса сохранения
        /// </summary>
        /// <param name="order"></param>
        private void SaveOrder(OrderEntity order)
        {
            var client = new ServicesClients.OrdersClient();
            try
            {
                // Выбираются старые товары - их количество
                var oldOrder = (order.Header.Id > 0) ? client.GetOrder(UserPrincipal.Current.UserId, order.Header.Id) : null;
                Dictionary<int, int> oldProductCounts = new Dictionary<int, int>();
                if (oldOrder != null)
                    oldProductCounts = oldOrder.Specification.Select(s => s.Specification).ToDictionary(s => s.ProductId, s => s.Count);


                int id = client.SaveOrder(UserPrincipal.Current.UserId, order);
                int sdraft = Convert.ToInt32(this.savedraft.Value);
                if (sdraft == 0)
                    Session["OkMessage"] = null;
                else
                    Session["OkMessage"] = "Заказ автоматически сохранен";

                // Если есть Красные строки - несуществующие заказы, то отсылать на почту менеджеру письмо-уведомление
                if (order.Header.StateId == (int)StateEnum.Project) // Если сохраняется заказ в статусе Проект
                {
                    order = client.GetOrder(UserPrincipal.Current.UserId, id);

                    if (order.Specification.Where(s => s.Specification.IsUnavailable.HasValue && s.Specification.IsUnavailable.Value == true).Any())
                    {
                        // Отправить письмо менеджеру компании Мегаофис с детальной информацией о Заказе
                        var processor = new MessageProcessor();
                        string tableOrder = ObjectToHtmlHelper.GetOrderHTML(order, true);

                        string fromAddressName = order.Header.ContactName; //UserPrincipal.Current.UserFullName;
                        var clientSettings = new SettingsClient();
                        var setting = clientSettings.GetSettingValue("ManagerEMail");
                        string email = (setting != null) ? setting.SettingValue : ConfigurationManager.AppSettings["AdminEmailAddress"];
                        processor.ProcessSendMessage(tableOrder, email, fromAddressName, string.Format("Заведен заказ № {0}, в котором есть позиции, отсутствующие в каталоге (строки выделяются красных фоном).", order.Header.Number));
                    }
                }

                if (this.ModifiedByAdmin && Request.QueryString["oid"] != null) // Заказ модифицируется администратором
                {
                    // Необходимо ставить статусы заказу
                    
                    // Выборка исполненных накладных, выписанных из данного Заказа.
                    List<ExpendBillEntity> ExpendBills = null;
                    ExpendBills = GetExpendBillEntityList(id);

                    var stClient = new StatesClient();
                    try
                    {
                        if (ExpendBills != null)
                        {
                            //Существуют исполненные накладные, заказ надо переводить в статус частично или полностью исполнен
                            if (IsFullDispatchOrder(order, ExpendBills.Where(b => b.State.Id == (int)StateEnum.ExpendBillExecuted).ToList())) // Все товары отгружены. Заказ переводится в статус Полностью отгружен
                            {
                                stClient.SetDocumentState(UserPrincipal.Current.UserId, (int)DocTypeEnum.Order, id, (int)StateEnum.FullDispatch);
                            }
                            else // Есть неотгруженные товары. Заказ переводится в статус Частично отгружен
                            {
                                stClient.SetDocumentState(UserPrincipal.Current.UserId, (int)DocTypeEnum.Order, id, (int)StateEnum.PartialDispatch);
                            }
                        }
                        else
                        {
                            // Не существует исполненных накладных. Заказ переводится в стастус Сформирован счет
                            stClient.SetDocumentState(UserPrincipal.Current.UserId, (int)DocTypeEnum.Order, id, (int)StateEnum.CreatedAccount);
                        }

                        #region Обновление накладных в статусе "Сохранена"

                        #region // 1) Удален товар из Заказа. Необходимо в цикле по Сохраненным Накладным проверить его наличие, удалить и пересчитать итогоые суммы.
                        var bClient = new ServicesClients.ExpendBillsClient();
                        foreach (var bill in ExpendBills.Where(b => b.State.Id == (int)StateEnum.ExpendBillSaved).ToList())
                        {
                            List<int> productIDs = new List<int>();
                            foreach (var bspec in bill.Specification)
                            {
                                if (!order.Specification.Where(os => os.Specification.ProductId == bspec.ProductId).Any())
                                    if (!productIDs.Contains(bspec.ProductId))
                                        productIDs.Add(bspec.ProductId);
                            }

                            if (productIDs.Count > 0)
                            {
                                try
                                {
                                    bClient.RemoveProductsFromExpendBill(UserPrincipal.Current.UserId, bill.ExpendBillId, productIDs.ToArray());
                                }
                                catch (System.ServiceModel.FaultException<ExceptionDetail> ex)
                                {
                                    string message = bClient.GetExceptionDetail(ex);
                                    this.ErrorMessage.Text = message;
                                }
                                catch (System.Exception exp)
                                {
                                    this.ErrorMessage.Text = exp.Message;
                                }
                            }
                        }
                        #endregion // 1) Удален товар из Заказа. Необходимо в цикле по Сохраненным Накладным проверить его наличие, удалить и пересчитать итогоые суммы.

                        // 2) Добавлен товар в Заказ.
                        //    !!!-Ничего не делаем-!!!, товар так и будет отображаться в накладных в правой таблице.

                        // 3) Увеличилось количество у товара в Заказе.
                        //    !!!-Ничего не делаем-!!!, остаточное количество товара так и будет отображаться в накладных в правой таблице.

                        #region // 3) Уменьшилось количество у товара в Заказе.
                        List<BillProductCountComposite> billsForModif = new List<BillProductCountComposite>(); // Словарь с Id накладной и количеством товара, которое надо вставить в поле количество товара
                        foreach (var ospec in order.Specification)
                        {
                            // Если ранее количество данного товара было больше, то отрабатывает функционал изменения сохраненных накладных
                            if (oldProductCounts.ContainsKey(ospec.Specification.ProductId) && oldProductCounts[ospec.Specification.ProductId] > ospec.Specification.Count)
                            {
                                int countInExecuted = ExpendBills.Where(b => b.State.Id == (int)StateEnum.ExpendBillExecuted).Select(b => b.Specification.Where(s => s.ProductId == ospec.Specification.ProductId).Select(s => s.Count).Sum()).Sum();
                                int productCount = ospec.Specification.Count - countInExecuted; // Количество товара, которые надо распределить по сохраненным накладным (кол-во в заказе за вычетом кол-ва в исполненных накладных

                                foreach (var bill in ExpendBills.Where(b => b.State.Id == (int)StateEnum.ExpendBillSaved).ToList())
                                {
                                    var bspec = bill.Specification.Where(s => s.ProductId == ospec.Specification.ProductId).FirstOrDefault();
                                    if (bspec != null) // В рассматриваемой накладной есть такой товар. Уменьшаем его количество
                                    {
                                        if (bspec.Count >= productCount) // количество, которое стало в заказе после уменьшения, полностью покрывается уменьшением количества в данной накладной. Запоминаем накладную и товар и переходим из цикла по накладным
                                        {
                                            if (billsForModif.Where(b => b.Id == bill.ExpendBillId).Any()) // Существует массив товаров с количествами для данной накладной
                                            {
                                                var billProductCountComposite = billsForModif.Where(b => b.Id == bill.ExpendBillId).FirstOrDefault();
                                                var productCountComposite = new ProductCount() { Id = ospec.Specification.ProductId, Count = productCount };
                                                billProductCountComposite.Products.ToList().Add(productCountComposite); // добавляется еще одно значение в массив товаров-количеств

                                            }
                                            else
                                            {
                                                // Создается новая пара ключ-значение для накладной-товар-количествоТовара
                                                List<ProductCount> productCountList = new List<ProductCount>();
                                                productCountList.Add(new ProductCount() { Id = ospec.Specification.ProductId, Count = productCount });
                                                var billProductCountComposite = new BillProductCountComposite() { Id = bill.ExpendBillId, Products = productCountList };
                                                billsForModif.Add(billProductCountComposite);
                                            }
                                            productCount = 0;
                                        }
                                        else // Товар в Заказе уменьшается и его количество стало больше, чем есть в данной накладной. Оставляем товар в Накладной, просто уменьшаем нераспределенное количество товара
                                        {
                                            //if (billsForModif.Where(b => b.Id == bill.ExpendBillId).Any()) // Существует массив товаров с количествами для данной накладной
                                            //{
                                            //    var billProductCountComposite = billsForModif.Where(b => b.Id == bill.ExpendBillId).FirstOrDefault();
                                            //    var productCountComposite = new Otgruzka.ExpendBillServRef.ProductCount() { Id = ospec.Specification.ProductId, Count = 0 };
                                            //    billProductCountComposite.Products.ToList().Add(productCountComposite); // добавляется еще одно значение в массив товаров-количеств
                                            //}
                                            //else
                                            //{
                                            //    // Создается новая пара ключ-значение для накладной-товар-количествоТовара
                                            //    List<Otgruzka.ExpendBillServRef.ProductCount> productCountList = new List<Otgruzka.ExpendBillServRef.ProductCount>();
                                            //    productCountList.Add(new Otgruzka.ExpendBillServRef.ProductCount() { Id = ospec.Specification.ProductId, Count = 0 });
                                            //    var billProductCountComposite = new Otgruzka.ExpendBillServRef.BillProductCountComposite() { Id = bill.ExpendBillId, Products = productCountList.ToArray() };
                                            //    billsForModif.Add(billProductCountComposite);
                                            //}
                                            // Просто уменьшаем счетчик нераспределенного количества
                                            productCount = productCount - bspec.Count;
                                        }
                                    }
                                }
                            }
                        }
                        
                        try
                        {
                            // Собранный массив с Id накладных, товаров и количество товаров, которые надо вставить в спецификации этих накладных, пересылаем сервису входящим параметром
                            bClient.UpdateProductsFromExpendBills(UserPrincipal.Current.UserId, billsForModif.ToArray());
                        }
                        catch (System.ServiceModel.FaultException<ExceptionDetail> ex)
                        {
                            string message = bClient.GetExceptionDetail(ex);
                            this.ErrorMessage.Text = message;
                        }
                        catch (System.Exception exp)
                        {
                            this.ErrorMessage.Text = exp.Message;
                        }
                        #endregion // 3) Уменьшилось количество у товара в Заказе.

                        #endregion Обновление накладных в статусе "Сохранена"
                    }
                    catch (System.ServiceModel.FaultException<ExceptionDetail> ex)
                    {
                        string message = stClient.GetExceptionDetail(ex);
                        this.ErrorMessage.Text = message;
                    }
                    catch (System.Exception exp)
                    {
                        this.ErrorMessage.Text = exp.Message;
                    }


                    if (order.Header.CreatedUserId != UserPrincipal.Current.UserId)
                    {
                        //Не забыть дать доступ к странице "SendToAuthor.aspx" в управлении правами длоступа !!!!!!!!!!!!!!!!!!!!!

                        // Необходимо открывать диалоговое окно с отсылкой уведомления автору заказа
                        ScriptManager.RegisterStartupScript(this, this.GetType(), "sendauthor_window", "sendauthor()", true);
                    }
                    else
                    {
                        Response.Redirect("~/WebForms/OrderDetails.aspx?oid=" + id.ToString(), true);
                    }
                }
                else
                {
                    Response.Redirect("~/WebForms/OrderDetails.aspx?oid=" + id.ToString(), true);
                }
            }
            catch (System.ServiceModel.FaultException<ExceptionDetail> ex)
            {
                string message = client.GetExceptionDetail(ex);
                this.ErrorMessage.Text = message;
            }
            catch (System.Exception exp)
            {
                this.ErrorMessage.Text = exp.Message;
            }
        }


        #region методы экспорта в Эксель

        private ExcelDocument prepareDocument()
        {
            var doc = new ExcelDocument();

            WindowProperties wp = new WindowProperties()
            {
                WindowHeight = 12015,
                WindowWidth = 19440,
                WindowTopX = 480,
                WindowTopY = 150
            };

            DocumentProperties dp = new DocumentProperties()
            {
                Author = "",
                LastAuthor = "",
                Company = "Компания МЕГА",
                Version = 1.00
            };

            doc.Header = new ExcelHeader()
            {
                WindowProperties = wp,
                DocumentProperties = dp
            };

            var workSheet = new Worksheet()
            {
                Name = "Товары",
                Table = new Common.Export.Table()
            };


            doc.Worksheets.Add(workSheet);

            return doc;
        }


        /// <summary>
        /// Создание стилей для оформления документа
        /// </summary>
        /// <param name="doc"></param>
        protected void createStyles(ExcelDocument doc)
        {
            var styleBold = new CellStyle()
            {
                ID = "bold",
                Borders = new Thickness(1),
                FontSize = 10,
                FontWeight = FontWeights.Bold,
                TextWrapping = TextWrapping.Wrap,
                FontStyle = FontStyles.Normal,
                FontFamily = new FontFamily("Arial")
            };
            var styleBoldNonBorder = new CellStyle()
            {
                ID = "boldNonBorder",
                FontSize = 10,
                FontWeight = FontWeights.Bold,
                TextWrapping = TextWrapping.Wrap,
                FontStyle = FontStyles.Normal,
                FontFamily = new FontFamily("Arial")
            };
            var style = new CellStyle()
            {
                ID = "normal",
                Borders = new Thickness(1, 1, 1, 1),
                FontSize = 9,
                FontWeight = FontWeights.Normal,
                TextWrapping = TextWrapping.Wrap,
                FontStyle = FontStyles.Normal,
                FontFamily = new FontFamily("Arial")
            };
            var boldCenterHeadFoot = new CellStyle()
            {
                ID = "boldCenterHeadFoot",
                Borders = new Thickness(1),
                FontSize = 10,
                FontWeight = FontWeights.Bold,
                TextWrapping = TextWrapping.Wrap,
                FontStyle = FontStyles.Normal,
                HorizontalAlignment = HorizontalAlignment.Center,
                FontFamily = new FontFamily("Arial")
            };

            var normalCenter = new CellStyle()
            {
                ID = "normalCenter",
                Borders = new Thickness(1, 1, 1, 1),
                FontSize = 9,
                FontWeight = FontWeights.Normal,
                TextWrapping = TextWrapping.Wrap,
                FontStyle = FontStyles.Normal,
                HorizontalAlignment = HorizontalAlignment.Center,
                FontFamily = new FontFamily("Arial")
            };

            var boldCenterRight = new CellStyle()
            {
                ID = "boldCenterRight",
                Borders = new Thickness(1, 1, 1, 1),
                FontSize = 10,
                FontWeight = FontWeights.Bold,
                TextWrapping = TextWrapping.Wrap,
                FontStyle = FontStyles.Normal,
                HorizontalAlignment = HorizontalAlignment.Center,
                FontFamily = new FontFamily("Arial")
            };

            doc.Styles.Add(styleBold);
            doc.Styles.Add(styleBoldNonBorder);
            doc.Styles.Add(style);
            doc.Styles.Add(boldCenterHeadFoot);
            doc.Styles.Add(boldCenterRight);
            doc.Styles.Add(normalCenter);
        }


        /// <summary>
        /// Оформление документа созданными стилями
        /// </summary>
        /// <param name="doc"></param>
        protected void setStyles(ExcelDocument doc)
        {
            var tbl = doc.Worksheets[0].Table;

            tbl.BeforeRows.ForEach(row => row.Cells.ForEach(cell => cell.StyleID = "boldNonBorder"));
            tbl.HeaderRows.ForEach(row => row.Cells.ForEach(cell => cell.StyleID = "boldCenterHeadFoot"));
            tbl.Rows.ForEach(row => row.Cells.ForEach(cell => cell.StyleID = "normal"));

            for (int i = 0; i < tbl.Rows.Count; ++i)
            {
                tbl.Rows[i].Cells[0].StyleID = "normal";
                tbl.Rows[i].Cells[1].StyleID = "normalCenter";
                tbl.Rows[i].Cells[2].StyleID = "normalCenter";

                //for (int j = 1; j < tbl.Columns.Count - 1; ++j)
                //{
                //    tbl.Rows[i].Cells[j].StyleID = "normalCenter";
                //}
                //tbl.Rows[i].Cells[tbl.Columns.Count - 1].StyleID = "boldCenterRight";
            }

            //tbl.Rows[tbl.Rows.Count - 1].Cells[0].StyleID = "bold";
            //for (int i = 1; i < tbl.Columns.Count; ++i)
            //{
            //    tbl.Rows[tbl.Rows.Count - 1].Cells[i].StyleID = "boldCenterHeadFoot";
            //}
        }


        /// <summary>
        /// Создание столбцов и заголовков формируемого файла
        /// </summary>
        /// <param name="table"></param>
        /// <param name="data"></param>
        protected void createExcelColumnsAndHeaders(Common.Export.Table table, OrderEntity data)
        {
            int index = 1;

            table.Columns.Add(new Column() { AutoFitWidth = 1, Width = 300.0, Index = index++ });
            table.Columns.Add(new Column() { AutoFitWidth = 1, Width = 150.0, Index = index++ });
            table.Columns.Add(new Column() { AutoFitWidth = 1, Width = 150.0, Index = index++ });

            Row beforeRow1 = new Row();
            beforeRow1.Cells.Add(new Cell() { Data = "ООО \"Мега\". Телефон: +7(495) 665-67-04, почта: statusvip@inbox.ru, время работы с 10 до 18 часов.", Type = "String", MergeAcross = 2 });
            Row beforeRow2 = new Row();
            beforeRow2.Cells.Add(new Cell() { Data = string.Format("Дата составления: {0}", DateTime.Now), Type = "String", MergeAcross = 2 });
            Row beforeRow3 = new Row();
            beforeRow3.Cells.Add(new Cell() { MergeAcross = 2 });
            table.BeforeRows.Add(beforeRow1);
            table.BeforeRows.Add(beforeRow2);
            table.BeforeRows.Add(beforeRow3);

            var headerrow = new Row();
            headerrow.Cells.Add(new Cell() { Data = "Наименование товара" });
            headerrow.Cells.Add(new Cell() { Data = "Артикул товара" });
            headerrow.Cells.Add(new Cell() { Data = "Количество товара" });
            table.HeaderRows.Add(headerrow);
        }


        /// <summary>
        /// Заполнение документа данными
        /// </summary>
        /// <param name="table"></param>
        /// <param name="data"></param>
        protected void appendExcelData(Common.Export.Table table, List<OrderSpecEntity> data)
        {
            table.Rows.AddRange(data.Select(row =>
                                    {
                                        var result = new Row();
                                        result.Cells.Add(new Cell()
                                                        {
                                                            Data = row.Specification.ProductName,
                                                            Type = typeof(string).Equals(row.Specification.ProductName.GetType()) ? "String" : "Number"
                                                        });
                                        result.Cells.Add(new Cell()
                                        {
                                            Data = row.Specification.SupplierArtikul,
                                            Type = (row.Specification.SupplierArtikul == null || typeof(string).Equals(row.Specification.SupplierArtikul.GetType())) ? "String" : "Number"
                                        });
                                        result.Cells.Add(new Cell()
                                        {
                                            Data = row.Specification.Count.ToString(),
                                            Type = typeof(string).Equals(row.Specification.Count.GetType()) ? "String" : "Number"
                                        });
                                        return result;
                                    }).ToArray());
        }


        /// <summary>
        /// Создание списка ссылок на загрузку файлов со списками товаров по поставщикам
        /// </summary>
        public void ExportToExcel()
        {   
            var data = Session["CurrentOrderEntity"] as OrderEntity; // DataLoader.GetReportDataTable();

            if (data != null)
            {
                FillDataSuppliers(data);

                DownloadFileList.DataSource = DataSuppliers;
                DownloadFileList.DataBind();
                DownloadFileList.Visible = true;
            }
        }


        /// <summary>
        /// Словарь со списками товаров для каждого поставщика в рамках конкретного Заказа
        /// </summary>
        protected Dictionary<string, List<OrderSpecEntity>> DataSuppliers = new Dictionary<string, List<OrderSpecEntity>>();


        /// <summary>
        /// Создание словаря со списками товаров для каждого поставщика в рамках конкретного Заказа
        /// </summary>
        /// <param name="data"></param>
        private void FillDataSuppliers(OrderEntity data)
        {
            if (data != null)
            {
                DataSuppliers = new Dictionary<string, List<OrderSpecEntity>>();

                // Создается словарь <название_поставщика, список_товаров_поставщика>
                foreach (var spec in data.Specification)
                {
                    string supplierName = spec.Specification.SupplierName;
                    if (DataSuppliers.ContainsKey(supplierName))
                    {
                        DataSuppliers[supplierName].Add(spec);
                    }
                    else
                    {
                        var list = new List<OrderSpecEntity>();
                        list.Add(spec);
                        DataSuppliers.Add(supplierName, list);
                    }
                }
            }
        }


        /// <summary>
        /// Создание файла с таблицей товаров по конкретному поставщику
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void DownloadFile_Click(object sender, EventArgs e)
        {
            if (DataSuppliers == null || DataSuppliers.Keys.Count == 0)
            {
                var data = Session["CurrentOrderEntity"] as OrderEntity; // DataLoader.GetReportDataTable();
                if (data != null)
                {
                    FillDataSuppliers(data);

                    var button = sender as LinkButton;
                    if (button != null /*&& !string.IsNullOrEmpty(button.CommandArgument)*/)
                    {
                        string supplierName = button.CommandArgument;
                        if (DataSuppliers.ContainsKey(supplierName))
                        {
                            // Узнаем список товаров конкретного рассматриваемого поставщика в данном Заказе
                            List<OrderSpecEntity> products = DataSuppliers[supplierName];

                            var doc = prepareDocument();

                            createStyles(doc);
                            createExcelColumnsAndHeaders(doc.Worksheets[0].Table, data);
                            appendExcelData(doc.Worksheets[0].Table, products);
                            setStyles(doc);

                            Response.Redirect(string.Format("~/ExcelReport.ashx?FileName={0}&ReportID={1}",
                                                            string.Format("Поставщик_{0}", supplierName),
                                                            ExcelLoader.SaveReport(Context, Guid.NewGuid(), doc.ToString())));
                        }
                        else
                        {
                            this.ErrorMessage.Text = "Поставщика с таким наименованием не найдено в списке продуктов данного заказа.";
                        }
                    }
                }
            }
        }


        #endregion методы экспорта в Эксель

    }
}